//
// Created by hjie on 23-6-1.
//

#include "udp_client.h"
#include "channel.h"
#include "loop_event.hpp"
#include <asm-generic/socket.h>
#include <cstdio>
#include <cstring>
#include <memory>
#include <netinet/in.h>
#include <sys/epoll.h>
#include <sys/socket.h>

UdpClient::UdpClient(LoopEvent * loop_event, const std::string &ip, uint16_t port)
{
    m_loop_event = loop_event;
    memset(&m_remote_addr, 0, sizeof(m_remote_addr));

    inet_pton(AF_INET, ip.c_str(), &m_remote_addr.sin_addr);
    m_remote_addr.sin_family = AF_INET;
    m_remote_addr.sin_port   = htons(port);
    m_remote_len = sizeof(m_remote_addr);
    m_data_buffer = nullptr;
}

UdpClient::~UdpClient()
{
    if (m_data_buffer)
    {
        delete m_data_buffer;
        m_data_buffer = nullptr;
    }
    if (m_channel)
    {
        m_channel->RemoveEvent();
    }
}

void UdpClient::SetOnMessage(OnUdpPacketMessage &&message_func)
{
    m_message_func = message_func;
}

void UdpClient::StartClient()
{
    m_udp_socket.Open();
    m_udp_socket.SetOpt();
    m_udp_socket.SetNonBlocking();

    m_data_buffer = new DataBuffer(m_udp_socket.GetSocketFd());

    m_channel = std::make_shared<Channel>(m_loop_event, m_udp_socket.GetSocketFd(), nullptr, std::bind(&UdpClient::DoReadMsg, this, std::placeholders::_1), nullptr);
    m_channel->AddEvent(EPOLLIN | EPOLLONESHOT | EPOLLERR);
}

void UdpClient::DoReadMsg(int64_t channel_id)
{
    int status = 0;
    struct sockaddr_in addr;
    socklen_t length = sizeof(addr);
    do
    {
        status = 0;
        memset(&addr, 0, sizeof(addr));
        m_data_buffer->ReadData((struct sockaddr*)&addr, &length, status);
        if (status < 0)
        {
            // has error
            printf("*********************************************ReadData Has Error*********************************************\n");
            break;
        }
        else
        {
            if (m_message_func && m_data_buffer->Length() > 0)
            {
                UdpBufferPacket packet;
                packet.buffer  = m_data_buffer;
                packet.sai     = (struct sockaddr*)&addr;
                packet.sai_len = length;
                m_message_func(&packet);
            }
            if (status == 0)
            {
                break;
            }
        }

    }while(true);
    m_channel->UpdateEvent(EPOLLIN | EPOLLONESHOT | EPOLLERR);
}

int UdpClient::DoSendMsg(const uint8_t *data, int length)
{
    if (m_data_buffer)
    {
        // use loop event push task to other thread
        return m_data_buffer->WriteData(data, length, (struct sockaddr*)&m_remote_addr, m_remote_len);
    }
    return 0;
}

void UdpClient::DoAsyncMsg(const uint8_t *data, int length)
{
    if (m_data_buffer)
    {
        m_loop_event->PushTask([this, &data, &length](){

            m_data_buffer->WriteData(data, length);
        });
    }
}

void UdpClient::DoSendMsg(struct sockaddr * addr, socklen_t addr_length, const uint8_t * data, int length)
{
    if (m_data_buffer)
    {
        m_data_buffer->WriteData(data, length, addr, addr_length);
    }
}
